package com.xy.tool.util;

import org.springframework.beans.BeanUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * Created by zsp on 2018/8/7.
 */
public class BeanUtilsEx {

    public static <T> T getBeanOrNull(String className) {
        if(!StringUtils.hasText(className)) {
            return null;
        }
        try {
            return (T) ClassUtils.forName(className, null).newInstance();
        } catch (ClassNotFoundException  | InstantiationException | IllegalAccessException e) {
            return null;
        }
    }

    /**
     *
     * @param source
     * @param target
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copyProperties(S source, T target) {
        copyProperties(source, target, false);
    }

    /**
     *
     * @param source
     * @param target
     * @param required
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copyProperties(S source, T target, boolean required) {
        if(required) {
            Objects.requireNonNull(source, "The source is required.");
            Objects.requireNonNull(target, "The target is required.");
        }
        if(source != null && target != null) {
            BeanUtils.copyProperties(source, target);
        }
    }

    public static <S, T> void copyProperties(S source, T target, String... ignoreProperties) {
        copyProperties(source, target, false, ignoreProperties);
    }

    /**
     *
     * @param source
     * @param target
     * @param required
     * @param ignoreProperties
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copyProperties(S source, T target, boolean required,
                                             String... ignoreProperties) {
        if(required) {
            Objects.requireNonNull(source, "The source is required.");
            Objects.requireNonNull(target, "The target is required.");
        }
        if(source != null && target != null) {
            BeanUtils.copyProperties(source, target, ignoreProperties);
        }
    }

    /**
     *
     * @param source
     * @param creator
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> T copyProperties(S source, Supplier<T> creator) {
        return copyProperties(source, creator, false);
    }

//    public static <S, T> T copyProperties(S source, Supplier<T> creator,BiConsumer<S,T> biConsumer) {
//        return copyProperties(source, creator, biConsumer, false);
//    }

    /**
     *
     * @param source
     * @param creator
     * @param required
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> T copyProperties(S source, Supplier<T> creator, boolean required) {
        Objects.requireNonNull(creator, "The creator is required.");
        if(required) {
            Objects.requireNonNull(source, "The source is required.");
        }
        if(source == null) {
            return null;
        }
        T target = creator.get();
        if(required) {
            Objects.requireNonNull(target, "The target is required.");
        }
        if(source != null && target != null) {
            BeanUtils.copyProperties(source, target);
        }
        return target;
    }


    /**
     *
     * @param source
     * @param creator
     * @param
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> T copyProperties(S source, Supplier<T> creator, BiConsumer<S, T> builder) {
        return copyProperties(source,creator,builder,false);
    }


    /**
     *
     * @param
     * @param creator
     * @param builder
     * @param required
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> T copyProperties(S source,
                                          Supplier<T> creator,
                                          BiConsumer<S, T> builder,
                                          boolean required) {
        Objects.requireNonNull(creator, "The creator is required.");
        if(required) {
            Objects.requireNonNull(source, "The source is required.");
        }
        if(source == null) {
            return null;
        }
        T target = creator.get();
        if(required) {
            Objects.requireNonNull(target, "The target is required.");
        }
        if(source != null && target != null) {
            BeanUtils.copyProperties(source, target);
            builder.accept(source,target);
        }
        return target;
    }

    /**
     *
     * @param sourceList
     * @param creator
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> List<T> copyPropertiesForNewList(Collection<S> sourceList,
                                                          Supplier<T> creator) {
        return copyPropertiesForNewList(sourceList, creator, false);
    }

    /**
     *
     * @param sourceList
     * @param creator
     * @param required
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> List<T> copyPropertiesForNewList(Collection<S> sourceList,
                                                          Supplier<T> creator,
                                                          boolean required) {
        if(CollectionUtils.isEmpty(sourceList)) {
            return new ArrayList<>();
        }
        Objects.requireNonNull(creator, "The creator is required.");
        List<T> targetList = new ArrayList<>();
        for(S source : sourceList) {
            targetList.add(copyProperties(source, creator, required));
        }
        return targetList;
    }

    /**
     *
     * @param sourceList
     * @param creator
     * @param builder
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> List<T> copyPropertiesForNewList(Collection<S> sourceList,
                                                          Supplier<T> creator,
                                                          BiConsumer<S, T> builder) {
        return copyPropertiesForNewList(sourceList, creator, builder,false);
    }

    /**
     *
     * @param sourceList
     * @param creator
     * @param builder
     * @param required
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> List<T> copyPropertiesForNewList(Collection<S> sourceList,
                                                          Supplier<T> creator,
                                                          BiConsumer<S, T> builder,
                                                          boolean required) {
        if(CollectionUtils.isEmpty(sourceList)) {
            return new ArrayList<>();
        }
        Objects.requireNonNull(creator, "The creator is required.");
        Objects.requireNonNull(creator, "The builder is required.");
        List<T> targetList = new ArrayList<>();
        T target;
        for(S source : sourceList) {
            target = copyProperties(source, creator, required);
            builder.accept(source, target);
            targetList.add(target);
        }
        return targetList;
    }

    /**
     *
     * @param sourceSet
     * @param creator
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> Set<T> copyPropertiesForNewSet(Set<S> sourceSet, Supplier<T> creator) {
        return copyPropertiesForNewSet(sourceSet, creator, false);
    }

    /**
     *
     * @param sourceSet
     * @param creator
     * @param required
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S, T> Set<T> copyPropertiesForNewSet(Set<S> sourceSet,
                                                        Supplier<T> creator,
                                                        boolean required) {
        if(CollectionUtils.isEmpty(sourceSet)) {
            return new HashSet<>();
        }
        Objects.requireNonNull(creator, "The creator is required.");
        Set<T> targetSet = new HashSet<>();
        for(S source : sourceSet) {
            targetSet.add(copyProperties(source, creator, required));
        }
        return targetSet;
    }

    /**
     *
     * @param sourceList
     * @param creator
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copyPropertiesForList(List<S> sourceList,
                                                    Supplier<List<T>> creator) {
        copyPropertiesForList(sourceList, creator, false);
    }

    /**
     *
     * @param sourceList
     * @param creator
     * @param required
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copyPropertiesForList(List<S> sourceList,
                                                    Supplier<List<T>> creator,
                                                    boolean required) {
        if(CollectionUtils.isEmpty(sourceList)) {
            return;
        }
        Objects.requireNonNull(creator, "The creator is required.");
        List<T> targetList = creator.get();
        if(CollectionUtils.isEmpty(targetList)) {
            return;
        }
        int size = sourceList.size() > targetList.size() ? targetList.size() : sourceList.size();
        for(int i = 0; i < size; i++) {
            S source = sourceList.get(i);
            T target = targetList.get(i);
            copyProperties(source, target, required);
        }
    }


    /**
     *
     * @param propertyName
     * @param obj
     */
    @SuppressWarnings("rawtypes")
    public static Object getValue(String propertyName, Object obj) {
        if (obj instanceof Map) {
            return ((Map) obj).get(propertyName);
        } else if (obj != null) {
            PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(obj.getClass(), propertyName);
            try {
                return pd.getReadMethod().invoke(obj, new Object[]{});
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

}
